/**
 * @file aiot_tcp_api.h
 * @brief TCP模块头文件, 提供用TCP协议向阿里云物联网平台上报数据的能力
 * @date 2019-12-27
 *
 * @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
 *
 */

#ifndef _AIOT_TCP_API_H_
#define _AIOT_TCP_API_H_

#if defined(__cplusplus)
extern "C" {
#endif

#include "core_stdinc.h"

/**
 * @brief 服务器返回的业务错误码
 *
 * @details
 *
 * 从云平台对上报消息回应的报文JSON中解析
 */
typedef enum {
    /**
     * @brief 0, 服务端成功接收到上报的消息
     */
    AIOT_TCP_RSPCODE_SUCCESS                   = 0,
    /**
     * @brief 10000, 服务端返回未知错误
     */
    AIOT_TCP_RSPCODE_COMMON_ERROR              = 10000,
    /**
     * @brief 10001, 请求参数错误
     */
    AIOT_TCP_RSPCODE_PARAM_ERROR               = 10001,
    /**
     * @brief 20001, token过期, 请调用 @ref aiot_tcp_auth 进行鉴权, 获取新token
     */
    AIOT_TCP_RSPCODE_TOKEN_EXPIRED             = 20001,
    /**
     * @brief 20002, 请求的header中无token可表明设备端合法, 请调用 @ref aiot_tcp_auth 进行鉴权, 获取新token
     */
    AIOT_TCP_RSPCODE_TOKEN_NULL                = 20002,
    /**
     * @brief 20003, token错误, 请调用 @ref aiot_tcp_auth 进行鉴权, 获取新token
     */
    AIOT_TCP_RSPCODE_TOKEN_CHECK_ERROR         = 20003,
    /**
     * @brief 30001, 消息上报失败
     */
    AIOT_TCP_RSPCODE_PUBLISH_MESSAGE_ERROR     = 30001,
    /**
     * @brief 40000, 设备端上报过于频繁, 触发服务端限流
     */
    AIOT_TCP_RSPCODE_REQUEST_TOO_MANY          = 40000,
} aiot_tcp_response_code_t;

/**
 * @brief @ref aiot_tcp_setopt 函数的 option 参数, 对于下文每一个选项中的数据类型, 指的是 @ref aiot_mqtt_setopt 中的data参数的数据类型
 *
 */
typedef enum {
    /**
     * @brief TCP 服务器的域名地址或者ip地址
     *
     * @details
     *
     * 阿里云物联网平台域名地址列表: (tcp使用80端口, tls使用443端口)
     *
     * |                域名地址               |  区域   | 端口号 |
     * |---------------------------------------|---------|--------|
     * | iot-as-http.cn-shanghai.aliyuncs.com  |  上海   |  443   |
     *
     * 数据类型: (char *)
     */
    AIOT_TCPOPT_HOST,
    /**
     * @brief TCP 服务器的端口号
     *
     * @details
     *
     * 连接阿里云物联网平台时:
     *
     * 1.如果使用的是tcp, 端口号设置为80
     *
     * 2. 如果使用的是tls, 端口号设置为443
     *
     * 数据类型: (uint16_t *)
     */
    AIOT_TCPOPT_PORT,
    /**
     * @brief TCP建联时, 网络使用的安全凭据
     *
     * @details
     *
     * 该配置项用于为底层网络配置 @ref aiot_sysdep_network_cred_t 安全凭据数据
     *
     * 1. 若该选项不配置, 那么MQTT将以tcp方式直接建联
     *
     * 2. 若 @ref aiot_sysdep_network_cred_t 中option配置为 @ref AIOT_SYSDEP_NETWORK_CRED_NONE , TCP将以tcp方式直接建联
     *
     * 3. 若 @ref aiot_sysdep_network_cred_t 中option配置为 @ref AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA , TCP将以tls方式建联
     *
     * 数据类型: (aiot_sysdep_network_cred_t *)
     */
    AIOT_TCPOPT_NETWORK_CRED,
    /**
     * @brief TCP建联时, 建立网络连接的超时时间
     *
     * @details
     *
     * 指建立socket连接的超时时间
     *
     * 数据类型: (uint32_t *) 默认值: (5 *1000) ms
     *
     */
    AIOT_TCPOPT_CONNECT_TIMEOUT_MS,
    /**
     * @brief TCP发送数据时, 在协议栈花费的最长时间
     *
     * @details
     *
     * 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
     */
    AIOT_TCPOPT_SEND_TIMEOUT_MS,
    /**
     * @brief TCP接收数据时, 在协议栈花费的最长时间
     *
     * @details
     *
     * 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
     */
    AIOT_TCPOPT_RECV_TIMEOUT_MS,
    /**
     * @brief 销毁TCP实例时, 等待其他api执行完毕的时间
     *
     * @details
     *
     * 当调用 @ref aiot_tcp_deinit 销毁TCP实例时, 若继续调用其他 aiot_tcp_xxx API, API会返回STATE_USER_INPUT_EXEC_DISABLED 错误
     *
     * 此时, 用户应该停止调用其他 aiot_tcp_xxx API
     *
     * 数据类型: (uint32_t *) 默认值: (2 * 1000) ms
     */
    AIOT_TCPOPT_DEINIT_TIMEOUT_MS,
    /**
     * @brief 当接收服务器返回的http报文时, 单行http header的最大长度
     *
     * @details
     *
     * 当单行http header设置过短时, @ref aiot_tcp_recv 会返回 @ref STATE_TCP_HEADER_BUFFER_TOO_SHORT 状态码
     *
     * 数据类型: (uint32_t *) 默认值: 128
     */
    AIOT_TCPOPT_HEADER_BUFFER_LEN,
    /**
     * @brief 当接收服务器返回的http报文时, 每次从 @ref aiot_tcp_recv_handler_t 回调函数中给出的body最大长度
     *
     * @details
     *
     * 数据类型: (uint32_t *) 默认值: 128
     */
    AIOT_TCPOPT_BODY_BUFFER_LEN,
    /**
     * @brief TCP 内部事件回调函数
     *
     * @details
     *
     * 数据类型: (aiot_tcp_event_handler_t)
     */
    AIOT_TCPOPT_EVENT_HANDLER,

    /* 以上选项配置的数据与 CORE_TCPOPT_XXX 共用 */

    /**
     * @brief 用户需要SDK暂存的上下文
     *
     * @details
     *
     * 1. 当接收到TCP数据时, 该上下文会从 @ref aiot_tcp_recv_handler_t 的 userdata 参数给出
     *
     * 2. 当TCP内部有事件发生时, 该上下文会从 @ref aiot_tcp_event_handler_t 的 userdata 参数给出
     *
     * 数据类型: (void *)
     */
    AIOT_TCPOPT_USERDATA,
    /**
     * @brief TCP 数据接收回调函数
     *
     * @details
     *
     * 数据类型: (aiot_tcp_recv_handler_t)
     */
    AIOT_TCPOPT_RECV_HANDLER,
    /**
     * @brief 设备的product key, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
     *
     * @details
     *
     * 数据类型: (char *)
     */
    AIOT_TCPOPT_PRODUCT_KEY,
    /**
     * @brief 设备的device name, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
     *
     * @details
     *
     * 数据类型: (char *)
     */
    AIOT_TCPOPT_DEVICE_NAME,
    /**
     * @brief 设备的device secret, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
     *
     * @details
     *
     * 数据类型: (char *)
     */
    AIOT_TCPOPT_DEVICE_SECRET,
    /**
     * @brief 上报设备的扩展信息, 比如模组商ID和模组ID
     *
     * @details
     *
     * 1. 模组商ID: 格式为pid=xxx
     *
     * 2. 模组ID: 格式为mid=xxx
     *
     * 如果需要同时上报多个信息, 那么它们之间用&连接, 例如: pid=xxx&mid=xxx
     *
     * 数据类型: (char *)
     */
    AIOT_TCPOPT_EXTEND_DEVINFO,
    /**
     * @brief 使用 @ref aiot_tcp_auth 进行认证并获取token的超时时间
     *
     * @details
     *
     * 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
     */
    AIOT_TCPOPT_AUTH_TIMEOUT_MS,
    /**
     * @brief 是否使用http长连接
     *
     * @details
     *
     * 若该配置的值为0, 则每次使用 @ref aiot_tcp_auth 和 @ref aiot_tcp_send 时, SDK会重新与 TCP 服务器建立简介
     *
     * 数据类型: (uint8_t *) 默认值: (5 * 1000) ms
     */
    AIOT_TCPOPT_LONG_CONNECTION,

    AIOT_TCPOPT_MAX
} aiot_tcp_option_t;

/**
 * @brief SDK收到TCP报文, 传递给用户数据回调函数时, 对报文类型的描述
 */
typedef enum {
    /**
     * @brief 获取到TCP Header, 每次返回Header中的一组键值对
     */
    AIOT_TCPRECV_AUTH,
    /**
     * @brief 获取到TCP Body, 返回完整的Body内容
     */
    AIOT_TCPRECV_BODY
} aiot_tcp_recv_type_t;

/**
 * @brief SDK收到TCP报文, 传递给用户数据回调函数时, 对报文内容的描述
 */
typedef struct {
    /**
     * @brief TCP 消息类型, 更多信息请参考 @ref aiot_tcp_recv_type_t
     */
    aiot_tcp_recv_type_t type;
    union {
        /**
         * @brief TCP 消息类型为 @ref AIOT_TCPRECV_AUTH 时的数据
         */
        struct {
            /**
             * @brief 单行 TCP Header 的 key
             */
            char *key;
            /**
             * @brief 单行 TCP Header 的 value
             */
            char *value;
        } header;
        /**
         * @brief TCP 消息类型为 @ref AIOT_TCPRECV_BODY 时的数据
         */
        struct {
            /**
             * @brief TCP Body 的内容
             */
            uint8_t *buffer;
            /**
             * @brief TCP Body 的长度
             */
            uint32_t len;
        } body;
    } data;
} aiot_tcp_recv_t;

/**
 * @brief TCP 消息接收回调函数原型, 可以通过 @ref aiot_tcp_setopt 接口的 @ref AIOT_TCPOPT_RECV_HANDLER 参数指定
 *
 * @details
 *
 * 当SDK收到 TCP 服务器的应答数据时, 通过此回调函数输出
 *
 * @param[out] handle TCP 句柄
 * @param[out] packet 从 TCP 服务器接收到的数据
 * @param[out] userdata 用户通过 @ref AIOT_TCPOPT_USERDATA 交由SDK暂存的上下文
 *
 * @return void
 */
typedef void (*aiot_tcp_recv_handler_t)(void *handle, const aiot_tcp_recv_t *packet, void *userdata);

/**
 * @brief SDK内部发生状态变化, 通过用户事件回调函数通知用户时, 对事件类型的描述
 */
typedef enum {
    /**
     * @brief token无效事件, 此时用户应该调用 @ref aiot_tcp_auth 获取新的token
     */
    AIOT_TCPEVT_TOKEN_INVALID
} aiot_tcp_event_type_t;

/**
 * @brief SDK内部发生状态变化, 通过用户事件回调函数通知用户时, 对事件内容的描述
 */
typedef struct {
    aiot_tcp_event_type_t type;
} aiot_tcp_event_t;

/**
 * @brief TCP 事件回调函数原型, 可以通过 @ref aiot_tcp_setopt 接口的 @ref AIOT_TCPOPT_EVENT_HANDLER 参数指定
 *
 * @param[out] handle TCP句柄
 * @param[out] event 事件结构体
 * @param[out] user_data 指向用户上下文数据的指针, 由 @ref aiot_tcp_setopt 的 @ref AIOT_TCPOPT_USERDATA 选项设置
 */
typedef void (* aiot_tcp_event_handler_t)(void *handle, const aiot_tcp_event_t *event, void *userdata);

/**
 * @brief 创建一个TCP上云实例
 *
 * @return void*
 *
 * @retval 非NULL, TCP 实例句柄
 * @retval NULL,初始化 TCP 实例失败
 */
void *aiot_tcp_init(void);

/**
 * @brief 设置TCP实例参数
 *
 * @param[in] handle TCP句柄
 * @param[in] option 配置选项, 更多信息请参考 @ref aiot_tcp_option_t
 * @param[in] data 配置数据, 更多信息请参考 @ref aiot_tcp_option_t
 *
 * @return int32_t
 *
 * @retval STATE_SUCCESS, 成功
 * @retval STATE_TCP_HANDLE_IS_NULL, TCP句柄为NULL
 * @retval STATE_USER_INPUT_OUT_RANGE, 用户输入参数无效
 * @retval STATE_SYS_DEPEND_MALLOC_FAILED, 内存分配失败
 */
int32_t aiot_tcp_setopt(void *handle, aiot_tcp_option_t option, void *data);

/**
 * @brief 向服务器发送认证请求, 获取token
 *
 * @param[in] handle TCP句柄
 *
 * @return int32_t
 *
 * @retval STATE_SUCCESS, 认证成功
 * @retval STATE_TCP_HANDLE_IS_NULL, TCP句柄为NULL
 * @retval STATE_USER_INPUT_MISSING_PRODUCT_KEY, 未设置必要选项ProductKey
 * @retval STATE_USER_INPUT_MISSING_DEVICE_NAME, 未设置必要选项DeviceName
 * @retval STATE_USER_INPUT_MISSING_DEVICE_SECRET, 未设置必要选项DeviceSecret
 * @retval STATE_TCP_TOKEN_LEN_ERROR, token长度错误
 * @retval STATE_TCP_GET_TOKEN_FAILED, 获取token失败
 */
int32_t aiot_tcp_connect_auth(void *handle, uint8_t *auth_data, uint32_t auth_len);

/**
 * @brief 上报数据到物联网平台
 *
 * @param[in] handle TCP句柄
 * @param[in] topic 上报的目标topic, 在物联网平台控制的产品详情页面有设备的完整topic列表
 * @param[in] payload 指向上报数据的指针
 * @param[in] payload_len 上报数据的长度
 *
 * @return int32_t
 *
 * @retval STATE_SUCCESS, 上报成功
 * @retval STATE_TCP_HANDLE_IS_NULL, TCP句柄为NULL
 * @retval STATE_USER_INPUT_OUT_RANGE, 用户输入参数无效
 * @retval STATE_TCP_NOT_AUTH, 设备未认证
 */
int32_t aiot_tcp_send(void *handle, uint8_t *payload, uint32_t payload_len);


/**
 * @brief 接受TCP应答数据, 数据会从用户设置的 @ref aiot_tcp_event_handler_t 回调函数输出
 *
 * @param[in] handle TCP句柄
 *
 * @return int32_t
 *
 * @retval >= 0, 接受到的TCP body数据长度
 * @retval STATE_TCP_HANDLE_IS_NULL, TCP句柄为NULL
 * @retval STATE_USER_INPUT_NULL_POINTER, 用户输入参数为NULL
 * @retval STATE_USER_INPUT_OUT_RANGE, buffer_len为0
 * @retval STATE_TCP_RSP_MSG_ERROR, 服务器应答消息错误
 * @retval STATE_SYS_DEPEND_NWK_CLOSED, 网络连接已关闭
 * @retval STATE_SYS_DEPEND_NWK_READ_OVERTIME, 网络接收超时
 * @retval STATE_TCP_RECV_LINE_TOO_LONG, TCP单行数据过长, 内部无法解析
 * @retval STATE_TCP_PARSE_STATUS_LINE_FAILED, 无法解析状态码
 * @retval STATE_TCP_GET_CONTENT_LEN_FAILED, 获取Content-Length失败
 */
int32_t aiot_tcp_recv(void *handle);

/**
 * @brief 销毁参数p_handle所指定的TCP实例
 *
 * @param[in] p_handle 指向TCP句柄的指针
 *
 * @return int32_t
 *
 * @retval STATE_SUCCESS 成功
 * @retval STATE_USER_INPUT_NULL_POINTER 参数p_handle为NULL或者p_handle指向的句柄为NULL
 */
int32_t aiot_tcp_deinit(void **p_handle);




/**
 * @brief -0x0500~-0x05FF表达SDK在HTTP模块内的状态码
 *
 */
#define STATE_TCP_BASE                                             (-0x0500)

/**
 * @brief 解析收到的HTTP报文时, 无法获取有效的状态行, 得不到HTTP StatusCode
 *
 */
#define STATE_TCP_STATUS_LINE_INVALID                              (-0x0501)

/**
 * @brief 解析收到的HTTP报文时, 报文的body部分已接收完毕, 没有更多数据
 *
 */
#define STATE_TCP_READ_BODY_FINISHED                               (-0x0502)

/**
 * @brief 执行@ref aiot_http_deinit 时, 等待其他API执行结束超过设定的超时时间, HTTP实例销毁失败
 *
 */
#define STATE_TCP_DEINIT_TIMEOUT                                   (-0x0503)

/**
 * @brief 因为HTTP认证应答的StatusCode不是200, 认证失败
 *
 */
#define STATE_TCP_AUTH_CODE_FAILED                                 (-0x0504)

/**
 * @brief 因为HTTP认证应答接收未完成, 认证失败
 *
 */
#define STATE_TCP_AUTH_NOT_FINISHED                                (-0x0505)

/**
 * @brief 因为HTTP认证应答中未能解析到Token, 认证失败
 *
 */
#define STATE_TCP_AUTH_TOKEN_FAILED                                (-0x0506)

/**
 * @brief 设备尚未认证, 需要先调用@ref aiot_http_auth 进行设备认证
 *
 */
#define STATE_TCP_NEED_AUTH                                        (-0x0507)

/**
 * @brief HTTP应答数据接收未完成, 需确认网络是否异常
 *
 */
#define STATE_TCP_RECV_NOT_FINISHED                                (-0x0508)

/**
 * @brief 内部buffer长度过短, 需调用@ref aiot_http_setopt 配置@ref AIOT_HTTPOPT_HEADER_BUFFER_LEN 选项增大buffer长度
 *
 */
#define STATE_TCP_HEADER_BUFFER_TOO_SHORT                          (-0x0509)

/**
 * @brief HTTP首部接收异常
 *
 */
#define STATE_TCP_HEADER_INVALID                                   (-0x050A)

/**
 * @brief HTTP首部发送相关日志使用的状态码
 *
 */
#define STATE_TCP_LOG_SEND_HEADER                                  (-0x050B)

/**
 * @brief HTTP内容发送相关日志使用的状态码
 *
 */
#define STATE_TCP_LOG_SEND_CONTENT                                 (-0x050C)

/**
 * @brief HTTP首部接收相关日志使用的状态码
 *
 */
#define STATE_TCP_LOG_RECV_HEADER                                  (-0x050D)

/**
 * @brief HTTP内容接收相关日志使用的状态码
 *
 */
#define STATE_TCP_LOG_RECV_CONTENT                                 (-0x050E)

/**
 * @brief HTTP连接断开相关日志使用的状态码
 *
 */
#define STATE_TCP_LOG_DISCONNECT                                   (-0x050F)

/**
 * @brief HTTP认证交互相关日志使用的状态码
 *
 */
#define STATE_TCP_LOG_AUTH                                         (-0x0510)

/**
 * @brief 因为HTTP应答报文的内容不符合预期, 认证失败
 *
 */
#define STATE_TCP_AUTH_NOT_EXPECTED                                (-0x0511)

/**
 * @brief HTTP报文的负载部分为空, 接收已完成
 *
 */
#define STATE_TCP_READ_BODY_EMPTY                                  (-0x0512)



#if defined(__cplusplus)
}
#endif

#endif /* #ifndef _AIOT_TCP_API_H_ */

